package org.example.java.ref;

import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.util.concurrent.TimeUnit;

/**
 * -verbose:gc -Xmx150M Created by Juntao.Zhang on 2014/9/10.
 */
public class ReferenceTest {

  static class BigData {

    byte[] data = new byte[1024 * 1024 * 50];

    public int size() {
      return data == null ? 0 : data.length;
    }

    @Override
    protected void finalize() throws Throwable {
      super.finalize();
      System.out.println("finalize 被调用");
    }
  }

  /**
   * 强引用 可以直接访问目标对象。 所指向的对象在任何时候都不会被系统回收。 可能导致内存泄漏。
   */
  static class Strong {

    public static void main(String[] args) {
      BigData data = new BigData();
      data = null;
      System.out.println("first gc");
      System.gc();

      System.out.println("second gc");
      System.gc();
    }
    /**
     * 覆写finalize
     [GC 686K->276K(15424K), 0.0014270 secs]
     [Full GC 276K->276K(15424K), 0.0042070 secs]
     first gc
     [Full GC 51737K->51476K(66692K), 0.0031600 secs]
     second gc
     finalize 被调用
     [Full GC 52851K->271K(124452K), 0.0022180 secs]

     [GC 686K->276K(15424K), 0.0013910 secs]
     [Full GC 276K->276K(15424K), 0.0021130 secs]
     first gc
     [Full GC 51737K->270K(66692K), 0.0027090 secs]
     second gc
     [Full GC 767K->270K(89732K), 0.0019390 secs]

     */

  }

  static class Strong2 {

    public static void main(String[] args) {
      byte[] tmp = new byte[1024 * 1024 * 50], tmp1 = tmp;
      System.gc();
      tmp = null;
      System.gc();
      tmp1 = null;
      System.gc();//真正的被回收
    }
  }

  /**
   * -verbose:gc -Xmx150M 软引用使用 get() 方法取得对象的强引用从而访问目标对象。 所指向的对象按照JVM的使用情况（Heap 内存是否临近阈值）来决定是否回收。
   * 可以避免 Heap 内存不足所导致的异常。
   */
  static class Soft {

    public static void main(String[] args) throws Exception {
      SoftReference<BigData> reference = new SoftReference<>(new BigData());
      System.out.println("gc by user");
      System.gc();  //gc 不会释放内存 只有在OutOfMemoryError之前释放
      TimeUnit.MILLISECONDS.sleep(200);
      System.out.println("after gc size is " + reference.get().size());
      TimeUnit.MILLISECONDS.sleep(200);
      BigData tmp = new BigData();
      System.out.println("memory is full, can't release any more, size is " + reference.get());
    }
  }


  static class Weak {

    static class Entry extends WeakReference<String> {

      Object value;

      Entry(String k, Object v) {
        super(k);
        value = v;
      }
    }

    static class MyWeakReference extends WeakReference<byte[]> {

      Byte[] value = new Byte[1024];

      MyWeakReference(byte[] referent) {
        super(referent);
      }
    }

    public static void main(String[] args) throws Exception {
//      WeakReference<byte[]> weakReference = new WeakReference<>(new byte[1024 * 1024 * 50]);
      byte[] bytes = new byte[1024 * 1024 * 50];
      MyWeakReference weakReference = new MyWeakReference(bytes);
//      String key = "test";
//      Entry weakReference = new Entry("test",new byte[1024 * 1024 * 50]);
//      System.out.printf("before gc length is %s.%n",weakReference.get().length/(1024*1024));
      bytes = null;
      System.out.printf("before gc length is %s.%n", weakReference.get());
      System.out.println("gc by user");
//      weakReference.clear();
      System.gc();
      TimeUnit.MILLISECONDS.sleep(20);
      System.out.printf("after gc value is %s.%n", weakReference.value.length);
      System.out.printf("after gc ref is %s.%n", weakReference.get());
//      System.out.println(bytes.length);
    }
  }

  /**
   * -verbose:gc -Xmx150M
   * 虚引用
   * 永远无法使用 get() 方法取得对象的强引用从而访问目标对象。
   * 所指向的对象在被系统内存回收前，虚引用自身会被放入ReferenceQueue对象中从而跟踪对象垃圾回收。
   * 不会根据内存情况自动回收目标对象。
   */
  static class Phantom {

    public static void main(String[] args) {
      ReferenceQueue<Object> refQueue = new ReferenceQueue<>();
      PhantomReference<Object> referent = new PhantomReference<>(new Object(), refQueue);
      PhantomReference<Object> referent2 = new PhantomReference<>(new Object(), refQueue);
      System.out.println("referent get" + referent.get());// null
      System.out.println("referent2 get" + referent2.get());// null
      System.gc();
//      System.runFinalization();
      System.out.println("referent=>" + referent);
      System.out.println("referent2=>" + referent2);
      System.out.println("poll=>" + refQueue.poll());
      System.out.println("poll=>" + refQueue.poll());
      System.out.println("poll=>" + refQueue.poll());
    }
  }
}
